﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DSS_XNA.Engine.Error;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Graphics;
using DSS_XNA.Engine.World.Worlds.RPGWorld;
using DSS_XNA.Engine.Actor;
using DSS_XNA.Engine.Entities;

namespace DSS_XNA.Engine.World
{
    public class RPGWorld : IWorld
    {
        #region IWorld Members

        public Game1 Engine
        {
            get { return _engine; }
            set { _engine = value; }
        }
        private Game1 _engine = null;

        public Timer.Timer LevelTimer
        {
            get { return _levelTimer; }
            set { _levelTimer = value; }
        }
        private Timer.Timer _levelTimer = new DSS_XNA.Engine.Timer.Timer();

        public List<IActor> Actors
        {
            get { return _actors; }
            set { _actors = value; }
        }
        private List<IActor> _actors = new List<IActor>();

        public List<Objects.Object> Objects
        {
            get { return _objects; }
            set { _objects = value; }
        }
        private List<Objects.Object> _objects = new List<DSS_XNA.Engine.Objects.Object>();

        public List<Objects.Object> ExpiredObjects
        {
            get { return _expiredObjects; }
            set { _expiredObjects = value; }
        }
        private List<Objects.Object> _expiredObjects = new List<DSS_XNA.Engine.Objects.Object>();

        public TileMap TileMap
        {
            get { return _tileMap; }
            set { _tileMap = value; }
        }
        private TileMap _tileMap = null;

        public TileSetManager TileSetManager
        {
            get { return _tileSetManager; }
            set { _tileSetManager = value; }
        }
        private TileSetManager _tileSetManager = new TileSetManager();

        public Entity FocusedEntity
        {
            get { return _focusedEntity; }
            set { _focusedEntity = value; }
        }
        private Entity _focusedEntity = null;

        public RPGWorld(string filePath, Game1 engine)
        {
            if (filePath == null)
                throw new DSSException("'filePath' is null.");
            if (filePath.Length == 0)
                throw new DSSException("'filePath' is zero-length.");
            if (engine == null)
                throw new DSSException("'engine' is null.");

            this.Load(filePath, engine);
        }

        public bool Load(string filePath, Game1 engine)
        {
            if (filePath == null)
                throw new DSSException("'filePath' is null.");
            if (filePath.Length == 0)
                throw new DSSException("'filePath' is zero-length.");
            if (engine == null)
                throw new DSSException("'engine' is null.");

            this.Engine = engine;

            this.Destroy();

            this.TileMap = new TileMap();
            this.TileMap.Load(filePath, this.TileSetManager, engine);

            return true;
        }

        public bool Render(SpriteBatch spriteBatch)
        {
            if (spriteBatch == null)
                throw new DSSException("'spriteBatch' is null.");

            this.TileMap.Render(spriteBatch, this.TileSetManager);

            foreach (IActor actor in this.Actors)
            {
                if (actor == this.FocusedEntity)
                {
                    Vector2 pos = actor.Position;

                    actor.Position.X = this.Engine.GraphicsDevice.ScissorRectangle.Center.X;
                    actor.Position.Y = this.Engine.GraphicsDevice.ScissorRectangle.Center.Y;

                    actor.Position.X -= actor.WidthInPixels / 2;
                    actor.Position.Y -= actor.HeightInPixels / 2;

                    actor.Render(spriteBatch);
                    actor.Position = pos;
                }
                else
                {
                    actor.Render(spriteBatch);
                }
            }

            return true;
        }

        public void Destroy()
        {
            if (this.TileMap != null)
            {
                this.TileMap = null;
            }

            foreach (IActor actor in this.Actors)
            {
                actor.Dispose();
            }
            this.Actors.Clear();

            foreach (DSS_XNA.Engine.Objects.Object o in this.Objects)
            {
                o.Dispose();
            }
            this.Objects.Clear();

            this.FocusedEntity = null;
        }

        public bool Update(GameTime gameTime, KeyboardState ks, MouseState ms, GamePadState gs)
        {
            if (gameTime == null)
                throw new DSSException("'gameTime' is null.");

            if (this.TileMap != null)
                this.TileMap.Update(gameTime, ks, ms, gs);

            foreach (DSS_XNA.Engine.Objects.Object obj in this.Objects)
            {
                obj.Update(gameTime, ks, ms, gs);
            }

            foreach (IActor actor in this.Actors)
            {
                actor.Update(gameTime, ks, ms, gs);
            }

            this.FocusOnEntity();

            return true;
        }

        public string GetProperty(string name)
        {
            if (name == null)
                throw new DSSException("'name' is null.");
            if (name.Length == 0)
                throw new DSSException("'name' is zero-length.");

            string property = String.Empty;

            name = name.ToLower().Trim();

            switch (name)
            {
                case "offsetx": property = this.TileMap.Offset.X.ToString(); break;
                case "offsety": property = this.TileMap.Offset.Y.ToString(); break;
                case "widthintiles": property = this.TileMap.MapWidthInTiles.ToString(); break;
                case "heightintiles": property = this.TileMap.MapHeightInTiles.ToString(); break;
                default:
                    throw new DSSException(String.Format("'{0}' is an unknown parameter.",
                name));
            }

            return property;
        }

        public int AddActor(IActor actor, int x, int y)
        {
            if (actor == null)
                throw new DSSException("'actor' is null.");

            x *= (int)(this.TileMap.DesiredTileWidthInMeters * World.Meter);
            y *= (int)(this.TileMap.DesiredTileHeightInMeters * World.Meter);

            actor.Position = new Vector2(x, y);

            this.Actors.Add(actor);
            return this.Actors.Count;
        }

        #endregion

        #region Private Methods

        private void FocusOnEntity()
        {
            if (this.FocusedEntity == null)
                return;

            Vector2 entityCenter = this.FocusedEntity.Position;
            entityCenter.X += this.FocusedEntity.WidthInPixels / 2;
            entityCenter.Y += this.FocusedEntity.HeightInPixels / 2;

            Point point = new Point();
            point.X = (int)entityCenter.X;
            point.Y = (int)entityCenter.Y;

            this.TileMap.FocusOnPoint(point);
        }

        #endregion
    }
}
